#include "nnsliteobjectcontainer.h"
#include "nnslitememory.h"

NNSLITE_RET nnsliteObjectContainerBaseCreate(nnsliteObjectContainerBase** base, nnsliteHeapFS* heapFS, u32 maxNumContainer,u32 bufSize)
{
	u32 baseMemSize = sizeof(nnsliteObjectContainerBase)+(maxNumContainer-1)*sizeof(nnsliteObjectContainer);
	*base = (nnsliteObjectContainerBase*)nnsliteMAlloc(baseMemSize);
	if (*base == NULL)
	{
		assert(false);
		return NNSLITE_RET_ERR_ALLOCATE_MEM;
	}
	(*base)->heapFS = heapFS;
	(*base)->bufSize = bufSize;
	(*base)->maxNumContainer = maxNumContainer;
	memset((*base)->containers, 0, maxNumContainer*sizeof(nnsliteObjectContainer));
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteObjectContainerBaseFree(nnsliteObjectContainerBase* base)
{
	u32 i;
	nnsliteObjectContainer* container;
	for (i=0; i<base->maxNumContainer; i++)
	{
		container = &base->containers[i];
		if (container->base != NULL) /* used */
			nnsliteBufferedHeapFileClose(&container->bhf);
	}
	nnsliteMFree(base);
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteObjectContainerBaseWrite(nnsliteObjectContainerBase* base, nnsliteBufferedHeapFile* bhf)
{
	u32 i;
	u32 hfdOfContainer;
	nnsliteObjectContainer* container;
	bool isUsed;
	/* Write the buffer size and maximum number of containers */
	nnsliteBufferedHeapFileWrite(bhf, &base->bufSize, sizeof(u32));
	nnsliteBufferedHeapFileWrite(bhf, &base->maxNumContainer, sizeof(u32));
	/* Write each container */
	for (i=0; i<base->maxNumContainer; i++)
	{
		container = &base->containers[i];
		if (container->base != NULL)
		{
			/* if this container is used */
			isUsed = true;
			nnsliteBufferedHeapFileWrite(bhf, &isUsed, sizeof(bool));
			hfdOfContainer = container->bhf.hf->hfd;
			nnsliteBufferedHeapFileWrite(bhf, &hfdOfContainer, sizeof(u32));
			nnsliteBufferedHeapFileWrite(bhf, &container->objCount, sizeof(u32));
			nnsliteBufferedHeapFileWrite(bhf, &container->containerID, sizeof(u32));
			nnsliteBufferedHeapFileFlush(&container->bhf);
		}
		else
		{
			/* if this container is not used */
			isUsed = false;
			nnsliteBufferedHeapFileWrite(bhf, &isUsed, sizeof(bool));
		}
	}
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteObjectContainerBaseRead(nnsliteObjectContainerBase** base,nnsliteHeapFS* heapFS, nnsliteBufferedHeapFile* bhf)
{
	NNSLITE_RET ret;
	u32 i;
	u32 hfdOfContainer;
	u32 bufSize, maxNumContainer;
	u32 baseMemSize;
	bool isUsed;
	nnsliteObjectContainer* container;
	/* Read the buffer size and maximum number of containers */
	nnsliteBufferedHeapFileRead(bhf, &bufSize, sizeof(u32));
	nnsliteBufferedHeapFileRead(bhf, &maxNumContainer, sizeof(u32));
	/* Allocate the object container base */
	baseMemSize = sizeof(nnsliteObjectContainerBase)+(maxNumContainer-1)*sizeof(nnsliteObjectContainer);
	*base = (nnsliteObjectContainerBase*)nnsliteMAlloc(baseMemSize);
	if (*base == NULL)
	{
		assert(false);
		return NNSLITE_RET_ERR_ALLOCATE_MEM;
	}
	(*base)->bufSize = bufSize;
	(*base)->maxNumContainer = maxNumContainer;
	(*base)->heapFS = heapFS;
	/* Read each container */
	for (i=0; i<maxNumContainer; i++)
	{
		container = &(*base)->containers[i];
		nnsliteBufferedHeapFileRead(bhf, &isUsed, sizeof(bool));
		if (isUsed)
		{
			container->base = *base;
			nnsliteBufferedHeapFileRead(bhf, &hfdOfContainer, sizeof(u32));
			nnsliteBufferedHeapFileRead(bhf, &container->objCount, sizeof(u32));
			nnsliteBufferedHeapFileRead(bhf, &container->containerID, sizeof(u32));
			/* Create the buffered heap file for this container */
			ret = nnsliteBufferedHeapFileOpen(heapFS, hfdOfContainer, &container->bhf, bufSize);
			NNSLITE_ON_FAILED_RETURN(ret);
		}
		else
		{
			container->base = NULL;
		}
	}

	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteObjectContainerCreate(nnsliteObjectContainerBase* base, u32* newContainerID)
{
	NNSLITE_RET ret;
	nnsliteObjectContainer* container;
	u32 i;
	/* Find a unused container */
	for (i=0; i<base->maxNumContainer; i++)
	{
		if (base->containers[i].base == NULL) /* If it is not used */
		{
			container = &base->containers[i];
			
			/* Create a buffered heap file  */
			ret = nnsliteBufferedHeapFileOpenNew(base->heapFS, &container->bhf, base->bufSize, U32_NULL);
			NNSLITE_ON_FAILED_RETURN(ret);
			/* Copy the index in the vector to be the container ID to the caller */
			container->containerID = i;
			container->base = base; /* Set the container be used */
			container->objCount = 0; /* Zero the count of objects */
			*newContainerID = i;
			return NNSLITE_RET_OK;		
		}
	}
	return NNSLITE_RET_ERR_NO_AVAILABLE;
}

NNSLITE_RET nnsliteObjectContainerDelete(nnsliteObjectContainerBase* base, u32 containerID)
{
	nnsliteObjectContainer* container = &base->containers[containerID];
	u32 hfd = container->bhf.hf->hfd;
	/* Close the buffered heap file structure */
	nnsliteBufferedHeapFileClose(&container->bhf);
	/* Delete the heap file */
	nnsliteHeapFileDelete(base->heapFS, hfd);
	/* Set the container be unused */
	container->base = NULL;
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteObjectContainerClose(nnsliteObjectContainer* container)
{
	NNSLITE_RET ret;
	
	/* Close the buffered heap file structure */
	ret = nnsliteBufferedHeapFileClose(&container->bhf);
	NNSLITE_ON_FAILED_RETURN(ret);

	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteObjectContainerInsert(nnsliteObjectContainer* container, const nnsliteObject* obj)
{
	nnsliteBufferedHeapFileWrite(&container->bhf, &obj->id, sizeof(u64));
	nnsliteBufferedHeapFileWrite(&container->bhf, &obj->size, sizeof(u32));
	nnsliteBufferedHeapFileWrite(&container->bhf, obj->data, obj->size);
	container->objCount++;
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteObjectContainerRemove(nnsliteObjectContainer* container, u32 objID)
{
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteObjectContainerBeginScan(nnsliteObjectContainer* container)
{
	return nnsliteBufferedHeapFileBeginRead(&container->bhf);
}

NNSLITE_RET nnsliteObjectContainerNext(nnsliteObjectContainer* container, nnsliteObject* obj)
{
	u64 objid;
	u32 objsize;

	assert(obj != NULL);

	nnsliteBufferedHeapFileRead(&container->bhf, &objid, sizeof(u64));
	nnsliteBufferedHeapFileRead(&container->bhf, &objsize, sizeof(u32));
	if (obj->size < objsize || obj->data == NULL)
	{
		if (obj->data != NULL)
			nnsliteMFree(obj->data);
		obj->data = nnsliteMAlloc(objsize);
		obj->size = objsize;
	}
	obj->size = objsize;
	obj->id = objid;
	nnsliteBufferedHeapFileRead(&container->bhf, obj->data, objsize);
	return NNSLITE_RET_OK;
}



